home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
FishMarket 1.0
/
FishMarket v1.0.iso
/
fishies
/
051-075
/
disk_055
/
pipedevice
/
pipe.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-06
|
13KB
|
351 lines
/*
* PIPE: device driver.
*
* Usage:
* The writer opens PIPE:somename and begins writing to it. The reader
* opens PIPE:samename and begins reading from it. It doesn't matter
* who opens PIPE:somename first. Note that if the writer opens the
* handle first, writes <BUFSIZE bytes, then closes, the Close() will
* not return until a reader has openned the same pipe.
*
* -Only two opens can be made on a specific pipe
* -One of the opens must always write while the other must always
* read.
*
* If the reader closed, any further writes will return an error
* If the writer closed, any further reads (after the buffer empties)
* will return 0.
*
* NOTE: Like the filesystem DOS device, I assume that no more than one
* request for a specific file handle will be queued at a time. This makes
* things a lot easier for me.
*
*
*/
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <exec/libraries.h>
#include <exec/devices.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <devices/console.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <libraries/filehandler.h>
typedef struct DosPacket DOSPACKET;
typedef struct Process PROC;
typedef struct DeviceNode DEVNODE;
typedef struct FileHandle FH;
typedef unsigned char u_char;
#define BUFSIZE 4096
#undef BADDR
#define BADDR(x) ((APTR)((long)x << 2))
#define ACTION_FIND_INPUT 1005L
#define ACTION_FIND_OUTPUT 1006L
#define ACTION_END 1007L
#define DOS_FALSE 0
#define DOS_TRUE -1
#define ST_EOF 0x01 /* Handle has been closed */
#define ST_WPEND 0x04 /* pending packet is a write */
#define ST_RPEND 0x08 /* pending packet is a read */
#define ST_CPEND 0x10 /* close pending (writer) */
#define OC_FIRST 1 /* first open, needs to be another */
#define OC_BOTH 2 /* both reader and writer open */
#define OC_LAST 3 /* one closed, one remaining */
#define OC_WAITSECOND 4 /* first open was closed before second was openned */
extern long AbsExecBase;
extern DOSPACKET *taskwait();
extern char *AllocMem();
long SysBase;
typedef struct _PIPE {
struct _PIPE *next, **prev;
DOSPACKET *pkt; /* Current pending packet, if any */
char buf[BUFSIZE]; /* Output Buffer */
char *name; /* name (allocated strlen(name)+1) */
short s, e, l; /* FIFO start, end, size */
char state; /* Current state */
char openstate;
} PIPE;
_main()
{
PROC *myproc; /* my process */
DOSPACKET *mypkt; /* a pointer to the dos packet sent */
BSTR parmdevname; /* pointer to device name in parmpkt Arg1 */
long parmextra; /* extra info passed in parmpkt Arg2 */
DEVNODE *mynode; /* our device node passed in parmpkt Arg3 */
FH *fh; /* a pointer to our file handle */
PIPE *pipe; /* current PIPE handle */
PIPE *Pipe = NULL;/* linked list base for all pipes */
char *str;
u_char *ptr;
long run = TRUE; /* handler main loop flag */
int ret; /* nominal packet return value */
int totalcnt = 0;/* total # active pipes */
SysBase = AbsExecBase;
myproc = (PROC *)FindTask(0L); /* find myself */
mypkt = taskwait(myproc); /* Wait for startup message */
parmdevname = (BSTR)mypkt->dp_Arg1; /* BSTR name passed to handler */
parmextra = mypkt->dp_Arg2; /* Extra Info passed */
mynode = (DEVNODE *)BADDR(mypkt->dp_Arg3); /* ptr to device node */
/* if taskid NOT installed, every ref creates new */
/* code must be reentrant */
mynode->dn_Task = &myproc->pr_MsgPort;
returnpkt(mypkt, myproc, DOS_TRUE, mypkt->dp_Res2);
while(run) {
mypkt = taskwait(myproc);
ret = DOS_TRUE;
pipe = (PIPE *)mypkt->dp_Arg1;
switch(mypkt->dp_Type) {
case ACTION_FIND_INPUT:
case ACTION_FIND_OUTPUT:
fh = (FH *)BADDR(mypkt->dp_Arg1); /* File handle */
ptr = (u_char *)BADDR(mypkt->dp_Arg3); /* File name */
str = AllocMem(*ptr + 1, 0);
if (str == NULL) {
ret = DOS_FALSE;
goto opfail;
}
bmov(ptr+1, str, *ptr);
str[*ptr] = 0;
for (pipe = Pipe; pipe; pipe = pipe->next) {
if (strcmp(pipe->name, str) == 0) {
FreeMem(str, *ptr + 1);
goto openok;
}
}
pipe = (PIPE *)AllocMem(sizeof(PIPE), 0);
if (pipe == NULL) {
ret = DOS_FALSE;
FreeMem(str, *ptr + 1);
goto opfail;
}
++totalcnt;
bzero(pipe, sizeof(*pipe));
pipe->l = BUFSIZE;
pipe->name = str;
if (Pipe)
Pipe->prev = &pipe->next;
pipe->next = Pipe;
pipe->prev = &Pipe;
Pipe = pipe;
openok:
switch(pipe->openstate) {
case 0:
pipe->openstate = OC_FIRST;
break;
case OC_FIRST:
pipe->openstate = OC_BOTH;
break;
case OC_WAITSECOND:
pipe->openstate = OC_LAST;
pipe->state &= ~ST_CPEND;
returnpkt(pipe->pkt, myproc, DOS_TRUE, pipe->pkt->dp_Res2);
break;
case OC_BOTH:
case OC_LAST:
ret = DOS_FALSE; /* more than 2 opens */
goto opfail;
}
fh->fh_Arg1 = (long)pipe;
fh->fh_Port = (struct MsgPort *)DOS_TRUE;
opfail:
returnpkt(mypkt, myproc, ret, mypkt->dp_Res2);
break;
case ACTION_END: /* If pending read, return pend bytes read
* If pending write, return pend bytes written
* return pending message, if any
*/
switch(pipe->openstate) {
case OC_FIRST:
pipe->openstate = OC_WAITSECOND;
break;
case OC_BOTH:
pipe->openstate = OC_LAST;
break;
case OC_LAST:
pipe->openstate = 0;
break;
}
pipe->state |= ST_EOF;
if (pipe->openstate == OC_WAITSECOND) {
pipe->pkt = mypkt;
pipe->state |= ST_CPEND;
break;
}
if (pipe->state & (ST_RPEND|ST_WPEND)) {
returnpktplain(pipe->pkt, myproc);
pipe->state &= ~(ST_RPEND|ST_WPEND);
}
if (pipe->openstate == 0) {
FreeMem(pipe->name, strlen(pipe->name)+1);
*pipe->prev = pipe->next;
if (pipe->next)
pipe->next->prev = pipe->prev;
FreeMem(pipe, sizeof(*pipe));
--totalcnt;
if (totalcnt == 0)
run = 0;
}
returnpkt(mypkt, myproc, ret, mypkt->dp_Res2);
break;
case ACTION_READ: /* Take chars from buffer. If buffer empty
* and read not satisfied, check for pending
* write and take bytes from it's buffer.
* When done, must check to see if buffer will
* hold ALL of pending write.
* When done, if read still is not satisfied,
* make it pending.
*/
mypkt->dp_Res1 = 0;
/*
* Load from buffer until empty or read fulfilled
*/
while (pipe->s != pipe->e && mypkt->dp_Res1 != mypkt->dp_Arg3) {
int avail = (pipe->s < pipe->e) ? pipe->e - pipe->s :
pipe->l - pipe->s;
int bytes = mypkt->dp_Arg3 - mypkt->dp_Res1;
if (bytes < avail)
avail = bytes;
bmov(pipe->buf+pipe->s, mypkt->dp_Arg2+mypkt->dp_Res1,avail);
pipe->s += avail;
mypkt->dp_Res1 += avail;
if (pipe->s == pipe->l)
pipe->s = 0;
}
/*
* If write packet was pending, the read will either exhaust
* the write and possibly become pending, or not exhaust the
* write and be returned.
*/
if (mypkt->dp_Res1 != mypkt->dp_Arg3 && (pipe->state & ST_WPEND)) {
int bytes = mypkt->dp_Arg3 - mypkt->dp_Res1;
int avail = pipe->pkt->dp_Arg3 - pipe->pkt->dp_Res1;
if (bytes > avail)
bytes = avail;
bmov(pipe->pkt->dp_Arg2 + pipe->pkt->dp_Res1, mypkt->dp_Arg2 + mypkt->dp_Res1, bytes);
mypkt->dp_Res1 += bytes;
pipe->pkt->dp_Res1 += bytes;
if (pipe->pkt->dp_Res1 == pipe->pkt->dp_Arg3) {
returnpktplain(pipe->pkt, myproc);
pipe->state &= ~ST_WPEND;
}
}
/* If read packet is made pending, buffer is always empty */
if (mypkt->dp_Res1 != mypkt->dp_Arg3 && !(pipe->state&ST_EOF)) {
if (pipe->state & (ST_RPEND|ST_WPEND|ST_CPEND)) {
returnpkt(pipe->pkt, myproc, DOS_FALSE, ERROR_OBJECT_IN_USE);
pipe->state &= ~(ST_RPEND|ST_WPEND|ST_CPEND);
}
pipe->pkt = mypkt;
pipe->state |= ST_RPEND;
} else {
returnpktplain(mypkt, myproc);
}
break;
case ACTION_WRITE: /*
* If pending read then buffer is empty, place
* chars directly into pending read. If pending
* write, error.
* If nothing pending and write buffer not big
* enough, make the write pend without filling
* the buffer. Otherwise, move write data into
* the buffer and return the packet.
*/
mypkt->dp_Res1 = 0;
if (pipe->state & ST_EOF) {
returnpkt(mypkt, myproc, DOS_FALSE, ERROR_SEEK_ERROR);
break;
}
if (pipe->state & ST_RPEND) { /* write->read */
int avail = mypkt->dp_Arg3 - mypkt->dp_Res1;
int bytes = pipe->pkt->dp_Arg3 - pipe->pkt->dp_Res1;
if (avail < bytes)
bytes = avail;
bmov(mypkt->dp_Arg2+mypkt->dp_Res1, pipe->pkt->dp_Arg2+pipe->pkt->dp_Res1, bytes);
mypkt->dp_Res1 += bytes;
pipe->pkt->dp_Res1 += bytes;
if (pipe->pkt->dp_Res1 == pipe->pkt->dp_Arg3) {
returnpktplain(pipe->pkt, myproc);
pipe->state &= ~ST_RPEND;
}
}
/* write into buffer */
while (mypkt->dp_Res1 != mypkt->dp_Arg3 && pipe->s != ((pipe->e+1)%pipe->l)) {
int avail = mypkt->dp_Arg3 - mypkt->dp_Res1;
int bytes;
if (pipe->e < pipe->s)
bytes = pipe->s - pipe->e - 1;
else
bytes = pipe->l - pipe->e - (pipe->s == 0);
if (avail < bytes)
bytes = avail;
bmov(mypkt->dp_Arg2 + mypkt->dp_Res1, pipe->buf + pipe->e, bytes);
pipe->e += bytes;
mypkt->dp_Res1 += bytes;
if (pipe->e == pipe->l)
pipe->e = 0;
}
if (mypkt->dp_Res1 != mypkt->dp_Arg3) {
if (pipe->state & (ST_RPEND|ST_WPEND|ST_CPEND)) {
returnpkt(pipe->pkt, myproc, DOS_FALSE, ERROR_OBJECT_IN_USE);
pipe->state &= ~(ST_RPEND|ST_WPEND|ST_CPEND);
}
pipe->pkt = mypkt;
pipe->state |= ST_WPEND;
} else {
returnpktplain(mypkt, myproc);
}
break;
default:
returnpkt(mypkt, myproc, DOS_FALSE, ERROR_ACTION_NOT_KNOWN);
break;
}
}
mynode->dn_Task = FALSE;
/* we are a process "so we fall off the end of the world" */
/* MUST fall through */
}